package com.rzjm.exercise.common.utils;

import com.rzjm.exercise.common.exception.InternalException;

import java.lang.annotation.Annotation;
import java.util.IdentityHashMap;
import java.util.Map;

/**
 * 类相关的工具类
 * 
 * @author zhangjr@sunwayworld.com 2014年5月23日
 */
public final class ClassUtils {
    /**
     * Map with primitive wrapper type as key and corresponding primitive type
     * as value, for example: Integer.class -> int.class.
     */
    private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new IdentityHashMap<Class<?>, Class<?>>(8);
    
    /**
     * Map with primitive type as key and corresponding wrapper type as value,
     * for example: int.class -> Integer.class.
     */
    private static final Map<Class<?>, Class<?>> primitiveTypeToWrapperMap = new IdentityHashMap<Class<?>, Class<?>>(8);
    
    // initialize
    static {
        primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
        primitiveWrapperTypeMap.put(Byte.class, byte.class);
        primitiveWrapperTypeMap.put(Character.class, char.class);
        primitiveWrapperTypeMap.put(Double.class, double.class);
        primitiveWrapperTypeMap.put(Float.class, float.class);
        primitiveWrapperTypeMap.put(Integer.class, int.class);
        primitiveWrapperTypeMap.put(Long.class, long.class);
        primitiveWrapperTypeMap.put(Short.class, short.class);
        
        primitiveWrapperTypeMap.forEach((w, p) -> primitiveTypeToWrapperMap.put(p, w));
    }
    
    /**
     * Get wrapper type of the specified primitive type
     */
    public static final Class<?> getWrapperType(Class<?> primitiveType) {
        return primitiveTypeToWrapperMap.get(primitiveType);
    }
    
    /**
     * Check whether the specified class is a primitive type
     */
    public static final boolean isPrimitiveType(Class<?> clazz) {
        return primitiveTypeToWrapperMap.containsKey(clazz);
    }
    
    /**
     * Check whether the specified class is a wrapper type
     */
    public static final boolean isWrapperType(Class<?> clazz) {
        return primitiveWrapperTypeMap.containsKey(clazz);
    }
    
    /**
     * Get primitive default value
     */
    public static final Object getPrimitiveDefaultValue(Class<?> primitiveType) {
        if (Boolean.TYPE == primitiveType) {
            return false;
        } else if (Byte.TYPE == primitiveType) {
            return '0';
        } else if (Character.TYPE == primitiveType) {
            return '0';
        } else if (Short.TYPE == primitiveType) {
            return Short.parseShort("0");
        } else if (Integer.TYPE == primitiveType) {
            return 0;
        } else if (Float.TYPE == primitiveType) {
            return 0f;
        } else if (Double.TYPE == primitiveType) {
            return 0d;
        } else if (Long.TYPE == primitiveType) {
            return 0l;
        } else {
            return null;
        }
    }
	
	
	/**
	 * 判断是否是基本类型或其包装类型
	 * 
	 * @param clazz 要判断的类
	 * @return true 如果是基本类型或包装类型
	 */
	public static boolean isPrimitiveOrWrapper(Class<?> clazz) {
		return isWrapperType(clazz) ||isPrimitiveType(clazz);
	}
	
	/**
	 * 转换基本类型为包装类型
	 * 
	 * @param type 基本类型
	 * @return 包装类型
	 */
	@SuppressWarnings("unchecked")
	public static <T> Class<T> primitiveToWrapper(final Class<T> type) {
        if (type == null || !type.isPrimitive()) {
            return type;
        }

        if (type == Integer.TYPE) {
            return (Class<T>) Integer.class;
        } else if (type == Double.TYPE) {
            return (Class<T>) Double.class;
        } else if (type == Long.TYPE) {
            return (Class<T>) Long.class;
        } else if (type == Boolean.TYPE) {
            return (Class<T>) Boolean.class;
        } else if (type == Float.TYPE) {
            return (Class<T>) Float.class;
        } else if (type == Short.TYPE) {
            return (Class<T>) Short.class;
        } else if (type == Byte.TYPE) {
            return (Class<T>) Byte.class;
        } else if (type == Character.TYPE) {
            return (Class<T>) Character.class;
        } else {
            return type;
        }
    }
	
	/**
	 * 转换包装类型为基本类型
	 * 
	 * @param type 基本类型
	 * @return 包装类型
	 */
	@SuppressWarnings("unchecked")
	public static <T> Class<T> wrapperToPrimitive(final Class<T> type) {
        if (type == null || type.isPrimitive()) {
            return type;
        }

        if (type == Integer.class) {
            return (Class<T>) Integer.TYPE;
        } else if (type == Double.class) {
            return (Class<T>) Double.TYPE;
        } else if (type == Long.class) {
            return (Class<T>) Long.TYPE;
        } else if (type == Boolean.class) {
            return (Class<T>) Boolean.TYPE;
        } else if (type == Float.class) {
            return (Class<T>) Float.TYPE;
        } else if (type == Short.class) {
            return (Class<T>) Short.TYPE;
        } else if (type == Byte.class) {
            return (Class<T>) Byte.TYPE;
        } else if (type == Character.class) {
            return (Class<T>) Character.TYPE;
        } else {
            return type;
        }
    }
	
	/**
	 * 获取对应{@link Class}的一个实例，该{@link Class}必须要有空的构造函数
	 * 
	 * @param clazz 需要创建实例的{@link Class}
	 * @return 一个实例
	 */
	public static final <T> T newInstance(final Class<T> clazz) {
		try {
			return clazz.newInstance();
		} catch (InstantiationException ie) {
			throw new InternalException(ie);
		} catch (IllegalAccessException iae) {
			throw new InternalException(iae);
		}
	}
	
	/**
     * 用于判断是否是Proxy
     * CGLib 代理  判断字段  {@code $$EnhancerByCGLIB$$}
     * JDK 代理  判断字段  {@code $Proxy}
     * 
     * @param clazz {@link Class}用来判断
     * @return {@code true} 如果是Proxy
     */
	public static final boolean isProxy(final Class<?> clazz) {
		if (clazz == null)
			return false;
		
		final String name = clazz.getName();
		
		return name.contains("$$EnhancerByCGLIB$$") ||
				name.contains("$Proxy"); 
	}
	
	/**
	 * 获取一个类的原始类，避免取得{@code proxy}
	 * 
	 * @param <T> 类名
	 * @param clazz 要被取原始类的{@link Class}
	 * @return 原生类
	 */
	@SuppressWarnings("unchecked")
	public static final <T> Class<T> getOriginalClass(final Class<T> clazz) {
		if (isProxy(clazz)) {
			return (Class<T>) clazz.getSuperclass();
		} else {
			return clazz;
		}
	}
	
	/**
	 * 对于数组获取数组中数据的类型
	 * 
	 * @param arrayClazz 数组的类 
	 * @return 数组中数据的实际类型
	 */
	@SuppressWarnings("unchecked")
	public static final <T> Class<T> getArrayClass(final Class<?> arrayClazz) {
		final String className = arrayClazz.getName();
		
		
		if ("[I".equals(className)) {
			return (Class<T>) Integer.TYPE;
		} else if ("[J".equals(className)) {
			return (Class<T>) Long.TYPE;
		} else if ("[D".equals(className)) {
			return (Class<T>) Double.TYPE;
		} else if ("[F".equals(className)) {
			return (Class<T>) Float.TYPE;
		} else if ("[C".equals(className)) {
			return (Class<T>) Character.TYPE;
		} else if ("[S".equals(className)) {
			return (Class<T>) Short.TYPE;
		} else if ("[Z".equals(className)) {
			return (Class<T>) Boolean.TYPE;
		} else if ("[B".equals(className)) {
			return (Class<T>) Byte.TYPE;
		} else if ("[V".equals(className)) {
			return (Class<T>) Void.TYPE;
		}
		
		return (Class<T>) arrayClazz;
	}
	
	/**
	 * 通过类名获取对应的类
	 * 
	 * @param className 类名
	 * @return 对应的类
	 */
	@SuppressWarnings("unchecked")
	public static final <T> Class<T> getClass(final String className) {
		final ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
		final ClassLoader loader = contextCL == null ? ClassLoader.getSystemClassLoader(): contextCL;
		
		try {
			final String abbreviation = getClassAbbreviation(className);
			if (className.equals(abbreviation)) { // 非基础类型
				return (Class<T>) Class.forName(getFullyQualifiedName(className), false, loader);
			} else { // 基础类型
				return (Class<T>) Class.forName("[" + abbreviation, false, loader).getComponentType();
			}
		} catch(ClassNotFoundException cnfe) {
			throw new InternalException(cnfe);
		}
	}
	
	/**
	 * 通过类名获取对应的类，如果不存在就返回{@code null}
	 * 
	 * @param className 类名
	 * @return 对应的类
	 */
	@SuppressWarnings("unchecked")
	public static final <T> Class<T> getClassIfPresent(final String className) {
		final ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
		final ClassLoader loader = contextCL == null ? ClassLoader.getSystemClassLoader(): contextCL;
		
		try {
			final String abbreviation = getClassAbbreviation(className);
			if (className.equals(abbreviation)) { // 非基础类型
				return (Class<T>) Class.forName(getFullyQualifiedName(className), false, loader);
			} else { // 基础类型
				return (Class<T>) Class.forName("[" + abbreviation, false, loader).getComponentType();
			}
		} catch(ClassNotFoundException cnfe) {
			return null;
		}
	}
	
	/**
	 * 是否是Java 自有的类型
	 * 
	 * @param clazz 要判断的类
	 * @return true 如果是JDK自有类型
	 */
	public static boolean isJdkType(final Class<?> clazz) {
		if (clazz == null)
			return true;
		
		final String name = clazz.getName();
		
		if (clazz.isPrimitive()
				|| !name.startsWith("com.sunwayworld."))
			return true;
		
		return false;	
	}
	
	/**
	 * 获取指定的类中注释的值，便利所有父类
	 * 
	 * @param clazz 要获取注释的类
	 * @param annotationClazz 注释的类
	 * @return 注释的值
	 */
	public static <A extends Annotation> A getAnnotation(final Class<?> clazz, final Class<A> annotationClazz) {
	    for (Class<?> cls = clazz; cls != null; cls = cls.getSuperclass()) {
	        final A annotation = cls.getAnnotation(annotationClazz);
	        
	        if (annotation != null) {
                return annotation;
            }
	    }
	    
	    return null;
	}
	
	/******************************************************************************************
	 * 内部私有的工具类
	 ******************************************************************************************/
	/**
	 * 获取类型的全部描述，用于从{@link ClassLoader}那里获取对应的{@link Class}
	 * 
	 * @param className 类名
	 * @return 类型的全部描述
	 */
	private static final String getFullyQualifiedName(final String className) {
		if (className.endsWith("[]")) { // 对于Array、Entry等要加以判断
			StringBuffer sb = new StringBuffer();

			String name = className;
			
			while(name.endsWith("[]")) {
				name = name.substring(0, name.length() - 2);
				
				sb.append("[");
			}
			
			final String abbreviation = getClassAbbreviation(name);
			
			if (StringUtils.isBlank(abbreviation)) {
				sb.append("L").append(name).append(";");
			} else {
				sb.append(abbreviation);
			}
			
			return sb.toString();
 		}
		
		return className;
	}
	
	/**
	 * 获取类名的简写，用于从{@link ClassLoader}那里获取对应的{@link Class}
	 * 
	 * @param className 类名
	 * @return 类名的简写，如果有的话
	 */
	private static final String getClassAbbreviation(final String className) {
		if ("int".equals(className)) {
			return "I";
		} else if ("long".equals(className)) {
			return "J";
		} else if ("double".equals(className)) {
			return "D";
		} else if ("float".equals(className)) {
			return "F";
		} else if ("char".equals(className)) {
			return "C";
		} else if ("short".equals(className)) {
			return "S";
		} else if ("boolean".equals(className)) {
			return "Z";
		} else if ("byte".equals(className)) {
			return "B";
		} else if ("void".equals(className)) {
			return "V";
		}
		
		return className;
	}
}

